﻿namespace Nintest.Builder
{
    using System;

    using JetBrains.Annotations;

    using Nintest.Model;

    [UsedImplicitly]
    public abstract class Tests<TFixture>
    {
        public interface IPreserve
        {
            IPreserveOrGivenOrWhen Preserve(Func<TFixture, bool> state);
            IPreserveOrGivenOrWhen Preserve<TArg>(Func<TFixture, TArg, bool> state, IDimension<TArg> param);
            IPreserveOrGivenOrWhen Preserve<TArg1, TArg2>(Func<TFixture, TArg1, TArg2, bool> state, IDimension<TArg1> arg1, IDimension<TArg2> arg2);
        }

        public interface IGiven
        {
            IGivenOrWhen Given(Func<TFixture, bool> state);
            IGivenOrWhen Given<TArg>(Func<TFixture, TArg, bool> state, IDimension<TArg> param);
            IGivenOrWhen Given<TArg1, TArg2>(Func<TFixture, TArg1, TArg2, bool> state, IDimension<TArg1> arg1, IDimension<TArg2> arg2);
        }

        public interface IWhen
        {
            IThen When(Action<TFixture> action);
            IThen When<TArg>(Action<TFixture, TArg> action, IDimension<TArg> param);
            IThen When<TArg1, TArg2>(Action<TFixture, TArg1, TArg2> action, IDimension<TArg1> param1, IDimension<TArg2> param2);

            IThen When<TResult>(UnboundDimension<TResult> result, Func<TFixture, TResult> action);
            IThen When<TArg, TResult>(UnboundDimension<TResult> result, Func<TFixture, TArg, TResult> action, IDimension<TArg> param);
            IThen When<TArg1, TArg2, TResult>(UnboundDimension<TResult> result, Func<TFixture, TArg1, TArg2, TResult> action, IDimension<TArg1> param1, IDimension<TArg2> param2);
        }

        public interface IPreserveOrGivenOrWhen : IPreserve, IGiven, IWhen
        {
        }

        public interface IGivenOrWhen : IGiven, IWhen
        {
        }

        public interface IThen
        {
            IThen Then(Func<TFixture, bool> state);
            IThen Then<TArg>(Func<TFixture, TArg, bool> state, IDimension<TArg> arg);
            IThen Then<TArg1, TArg2>(Func<TFixture, TArg1, TArg2, bool> state, IDimension<TArg1> arg1, IDimension<TArg2> arg2);
        }

        public interface IImplies
        {
            IImplies Implies(Func<TFixture, bool> state);
            IImplies Implies<TArg>(Func<TFixture, TArg, bool> state, IDimension<TArg> arg);
            IImplies Implies<TArg1, TArg2>(Func<TFixture, TArg1, TArg2, bool> state, IDimension<TArg1> arg1, IDimension<TArg2> arg2);
        }

        protected static UnboundDimension<T> Parameter<T>(string name)
        {
            return new UnboundDimension<T>(name);
        }

        protected static BoundDimension<T> Literal<T>(T arg)
        {
            return new BoundDimension<T>(arg);
        }

        [StringFormatMethod("format")]
        protected static BoundDimension<string> LiteralString(string format, params object[] args)
        {
            var arg = string.Format(format, args);
            return Literal(arg);
        }

        protected abstract void Build();

        [StringFormatMethod("name")]
        protected IPreserveOrGivenOrWhen Test(string name)
        {
            throw new NotImplementedException();
        }

        [StringFormatMethod("name")]
        protected IPreserveOrGivenOrWhen Test<TArg>(string name, IDimension<TArg> parameter)
        {
            throw new NotImplementedException();
        }

        [StringFormatMethod("name")]
        protected IPreserveOrGivenOrWhen Test<TArg1, TArg2>(string name, IDimension<TArg1> parameter1, IDimension<TArg2> parameter2)
        {
            throw new NotImplementedException();
        }

        protected void Case(Func<TFixture, bool> state)
        {
        }

        protected void Case<TArg>(Func<TFixture, TArg, bool> state, TArg actual)
        {
        }

        protected void Case<TArg1, TArg2>(Func<TFixture, TArg1, TArg2, bool> state, TArg1 actual1, TArg2 actual2)
        {
        }

        [StringFormatMethod("name")]
        protected IImplies State(Func<TFixture, bool> state, string name)
        {
            throw new NotImplementedException();
        }

        [StringFormatMethod("name")]
        protected IImplies State<TArg>(Func<TFixture, TArg, bool> state, string name, IDimension<TArg> parameter)
        {
            throw new NotImplementedException();
        }

        [StringFormatMethod("name")]
        protected IImplies State<TArg1, TArg2>(Func<TFixture, TArg1, TArg2, bool> state, string name, IDimension<TArg1> parameter1, IDimension<TArg2> parameter2)
        {
            throw new NotImplementedException();
        }
    }
}